app.controller("flowMapCtrl", [
  "$scope",
  "$stateParams",
  "$modal",
  "$rootScope",
  "$filter",
  "$http",
  "lodash",
  "$timeout",
  function(
    $scope,
    $stateParams,
    $modal,
    $rootScope,
    $filter,
    $http,
    lodash,
    $timeout
  ) {
    $scope.setBreadcrumb = function(title) {
      $rootScope.initBreadcrumb($scope.breadcrumb);
      if (title) {
        $scope.breadcrumb.push("场景地图", title);
      } else {
        $scope.breadcrumb.push("场景地图");
      }
    };
    $scope.setBreadcrumb();

    //页面加载时，给headerElement设置元素
    $rootScope.initHeaderElements($scope.settings);
    $scope.initSetting = function() {
      $rootScope.initHeaderElements($scope.settings);
      $scope.settings.btnEle.push({
        show: true,
        label: "返回",
        func: function() {
          $rootScope.backHistory();
        }
      });
    };

    $scope.initSetting();

    $scope.info = {
      diagramScope: {},
      sett: {},
      flowData: [],
      nodeDetails: {}, //节点详情信息
      flowArray: [], //点击节点下流程数组
      legendArray: [], //点击节点下图例数组
      showName: "map"
    };

    getFlowTree();

    //获取流程树数据
    function getFlowTree() {
      var url = matrix_product_url + "/flow/getFlowTree";
      $rootScope.showPanelLoading();
      $http.get(url).success(function(result) {
        if (result.code == "0") {
          if (!result.data) {
            $rootScope.toasterErrorInfo("获取流程树数据错误！");
          } else {
            $scope.info.flowData = setFlowTree([result.data], []);
          }
        } else {
          $rootScope.toasterErrorInfo(result.desc);
        }
        $scope.info.nodeDetails = "";
        $rootScope.hidePanelLoading();
      });
    }

    //解析流程树
    function setFlowTree(flowData, flowArray) {
      angular.forEach(flowData, function(flowItem, index) {
        flowItem.key = flowItem.id;
        flowItem.parent = flowItem.parentId;
        flowArray.push(flowItem);
        if (flowItem.children && flowItem.children.length) {
          setFlowTree(flowItem.children, flowArray);
        }
      });
      return flowArray;
    }

    //获取节点点击数据详情
    $scope.getNodeDetails = function(data) {
      console.log(data);
      if (null == data) return;
      var currentNodeChildren = [];
      var flowArray = [];
      var legendArray = [];
      data.typeName = data.type == "1" ? "流程架构" : "流程";
      $scope.info.nodeDetails = data;

      currentNodeChildren = setFlowTree(data.children, []);
      angular.forEach(currentNodeChildren, function(node) {
        if (node.parentId == data.id) {
          if (node.type == "1") {
            flowArray.push(node);
          } else {
            legendArray.push(node);
          }
        }
      });
      $scope.info.flowArray = flowArray;
      $scope.info.legendArray = legendArray;
      $scope.$apply();
    };

    //移动
    $scope.move = function(nodeData) {
      var status = nodeData.type == "1" ? "frame" : "flow";
      openModel("move", status, nodeData);
    };

    //添加架构
    $scope.addFlowFrame = function(nodeData) {
      openModel("add", "frame", nodeData);
    };

    //添加流程
    $scope.addFlow = function(nodeData) {
      openModel("add", "flow", nodeData);
    };

    //修改标题
    $scope.updateTitle = function(nodeData) {
      console.log(nodeData);
      var status = nodeData.type == "1" ? "frame" : "flow";
      openModel("edit", status, nodeData);
    };

    //打开模态框(type: add/edit，status: frame/flow, nodeData: 当前节点信息)
    function openModel(type, status, nodeData) {
      if (!nodeData) {
        $rootScope.toasterWarningInfo("请先选择要操作的数据，然后添加！");
        return false;
      }

      var modalInstance = $modal.open({
        animation: true,
        templateUrl: getTemplateUrl("matrix/component/tpl/flowNodeModal.html"),
        controller: "editFlowCtrl",
        keyboard: true,
        backdrop: true,
        resolve: {
          type: function() {
            return type;
          },
          status: function() {
            return status;
          },
          nodeData: function() {
            return nodeData;
          }
        }
      });

      modalInstance.result.then(
        function(nodeName) {
          if (status == "frame") {
            editFrameDetails(nodeName, type);
          } else {
            editFlowDetails(nodeName, type);
          }
        },
        function(reason) {
          console.log("关闭modal");
        }
      );
    }

    $scope.addNodeToTree = function(data) {
      var node = setFlowTree([data], [])[0];
      $scope.info.diagramScope.createNode(node);
    };

    //编辑流程架构节点信息
    function editFrameDetails(nodeName, type) {
      var postParams = {
        name: nodeName,
        type: "FLOW_FRAME", //1：架构  2：流程
        isDel: "1",
        level: "" + (parseInt($scope.info.nodeDetails.level) + 1),
        children: [],
        parentId: $scope.info.nodeDetails.id
      };
      if (type == "edit") {
        $scope.info.nodeDetails.name = nodeName;
        postParams = $scope.info.nodeDetails;
      }
      if (type == "move") {
        postParams.id = $scope.info.nodeDetails.id;
        postParams.name = $scope.info.nodeDetails.name;
        postParams.level = nodeName.level;
        postParams.parentId = nodeName.id;
        postParams.children = nodeName.children;
      }
      var url = matrix_product_url + "/flowFrame/saveFlowFrame";
      $http({
        method: "post",
        url: url,
        data: JSON.stringify(postParams),
        headers: { "Content-Type": "application/json" }
      }).success(function(result) {
        if (result.code == "0") {
          if ("add" == type) {
            $http
              .get(
                matrix_product_url +
                  "/flowFrame/getTreeNode?flowFrameId=" +
                  result.data
              )
              .success(function(result) {
                if (result.code == "0") {
                  $scope.addNodeToTree(result.data);
                }
              });
          } else if ("edit" == type) {
            $scope.info.diagramScope.updateNode();
          } else if ("move" == type) {
            $http
              .get(
                matrix_product_url +
                  "/flowFrame/getTreeNode?flowFrameId=" +
                  postParams.id
              )
              .success(function(result) {
                if (result.code == "0") {
                  $scope.info.diagramScope.moveNode(
                    setFlowTree([result.data], [])[0]
                  );
                }
              });
          }
          $rootScope.toasterSuccessInfo(
            type == "add"
              ? "架构添加成功！"
              : type == "move"
                ? "移动成功！"
                : "修改成功！"
          );
        } else {
          $rootScope.toasterErrorInfo(result.desc);
        }
      });
    }

    //编辑流程节点信息
    function editFlowDetails(nodeName, type) {
      var postParams = {
        name: nodeName,
        reqId: "-1",
        status: "ADD",
        attrs: {},
        bussinessId: $scope.info.nodeDetails.id
      };
      if (type == "edit") {
        $scope.info.nodeDetails.name = nodeName;
        postParams = $scope.info.nodeDetails;
        postParams.reqId = "-1";
      }
      if (type == "move") {
        postParams.id = $scope.info.nodeDetails.id;
        postParams.name = $scope.info.nodeDetails.name;
        postParams.bussinessId = nodeName.id;
      }
      var url =
        matrix_product_url +
        (type == "add" ? "/flow/createFlow" : "/flow/updateMasterFlow");
      $http({
        method: "post",
        url: url,
        data: JSON.stringify(postParams),
        headers: { "Content-Type": "application/json" }
      }).success(function(result) {
        if (result.code == "0") {
          if ("add" == type) {
            $http
              .get(
                matrix_product_url +
                  "/flow/getFlowTreeNode?flowId=" +
                  result.data.id
              )
              .success(function(result) {
                if (result.code == "0") {
                  $scope.addNodeToTree(result.data);
                }
              });
          } else if ("edit" == type) {
            $scope.info.diagramScope.updateNode();
          } else if ("move" == type) {
            $http
              .get(
                matrix_product_url +
                  "/flow/getFlowTreeNode?flowId=" +
                  postParams.id
              )
              .success(function(result) {
                if (result.code == "0") {
                  $scope.info.diagramScope.moveNode(
                    setFlowTree([result.data], [])[0]
                  );
                }
              });
          }
          $rootScope.toasterSuccessInfo(
            type == "add"
              ? "流程添加成功！"
              : type == "move"
                ? "流程移动成功！"
                : "流程编辑成功！"
          );
        } else {
          $rootScope.toasterErrorInfo(result.desc);
        }
      });
    }

    //绘制流程
    $scope.drawFlow = function(nodeData) {
      $scope.info.showName = "draw";
      $rootScope.initHeaderElements($scope.settings);
      var saveBtn = {
        show: true,
        label: "保存",
        func: function() {
          updateFlowDetails();
        }
      };
      var backUrl = {
        show: true,
        label: "返回",
        func: function() {
          $scope.info.showName = "map";
          $scope.setBreadcrumb();
          $scope.initSetting();
        }
      };
      $scope.settings.btnEle.push(saveBtn);
      $scope.settings.btnEle.push(backUrl);

      $scope.setBreadcrumb("绘制流程");

      $scope.draw = {
        currentReqId: "-1",
        flowTypeArray: [
          { crtname: "功能点", type: "feature" },
          { crtname: "流程", type: "flow" }
        ],
        sltLegendItem: "", //选择的图例数据
        isFeature: true,
        featureList: [], //全部功能点
        flowArray: [],
        diagram: null,
        flowDetails: "", //流程详情
        nameAndVersion: "",
        sltFlowTypeItem: { crtname: "功能点", type: "feature" },
        nodeDataArray: [],
        linkDataArray: [],
        customLegendArray: [
          { feature_lengend_id: "c01", category: "Start", text: "开始" },
          { feature_lengend_id: "c02", category: "Next", text: "下一步" },
          {
            feature_lengend_id: "c03",
            category: "Diamond",
            text: "判断",
            figure: "Diamond"
          },
          { feature_lengend_id: "c04", category: "End", text: "结束" },
          { feature_lengend_id: "c05", category: "Comment", text: "描述" },
          {
            feature_lengend_id: "c06",
            category: "Line",
            text: "线",
            length: 50
          },
          {
            feature_lengend_id: "c08",
            category: "vLine",
            text: "竖线",
            length: 50
          },
          { feature_lengend_id: "c07", category: "User", text: "用户" }
        ],
        masterFlowDatas: {},
        diagramScope: {}
      };

      $scope.draw.model = new go.GraphLinksModel([], []);
      $scope.draw.model.selectedFlowData = null;
      $scope.draw.mDiagram = null;
      $scope.draw.paModel = new go.GraphLinksModel(
        $scope.draw.customLegendArray
      );

      getFlowDetails();

      //获取流程详细信息
      function getFlowDetails() {
        var url =
          matrix_product_url +
          "/flow/getFlowDetail?flowId=" +
          nodeData.id +
          "&reqId=-1";
        $rootScope.showPanelLoading();
        $http.get(url).success(function(result) {
          if (result.code == "0") {
            // getFeatureList();
            // getFlowTree();
            if (!result.data) {
              $rootScope.toasterWarningInfo("获取流程详情数据错误！");
              return false;
            }
            $scope.draw.masterFlowDatas = {
              nodeDataArray: result.data.attrs.nodeDataArray || [
                { key: 1, text: "Start", category: "Start" }
              ],
              linkDataArray: result.data.attrs.linkDataArray || []
            };

            $scope.draw.flowDetails = result.data;
            $scope.draw.nameAndVersion = {
              name: result.data.name,
              version: result.data.attrs.version
            };
            if (!result.data.attrs) {
              $scope.draw.model = new go.GraphLinksModel([], []);
              return false;
            }
            $scope.draw.nodeDataArray = result.data.attrs.nodeDataArray;
            $scope.draw.linkDataArray = result.data.attrs.linkDataArray;
            $scope.draw.model = new go.GraphLinksModel(
              result.data.attrs.nodeDataArray,
              result.data.attrs.linkDataArray
            );
            $rootScope.hidePanelLoading();
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
        });
      }

      //更新flow节点的详细信息（更新flow的attrs）
      function updateFlowDetails() {
        $scope.draw.diagramScope.save();
        console.log("外部调用获取里面的值");
        var nodeDataArray = JSON.parse($scope.draw.diagramScope.flowData);
        console.log(nodeDataArray.nodeDataArray);
        console.log(nodeDataArray.linkDataArray);
        var currentDate = Number(new Date());
        var postParams = {
          id: nodeData.id,
          reqId: $scope.draw.currentReqId,
          attrs: {
            date: currentDate,
            version: $scope.draw.flowDetails.attrs.version,
            req_snapshot: $scope.draw.flowDetails.attrs.req_snapshot,
            master_version: $scope.draw.flowDetails.attrs.master_version,
            nodeDataArray: nodeDataArray.nodeDataArray || [],
            linkDataArray: nodeDataArray.linkDataArray || []
          }
        };
        console.log(JSON.stringify(preventCircularJson(postParams)));
        var url = matrix_product_url + "/flow/updateMasterFlow";
        $http({
          method: "post",
          url: url,
          data: JSON.stringify(postParams),
          headers: { "Content-Type": "application/json" }
        }).success(function(result) {
          if (result.code == "0") {
            $rootScope.toasterSuccessInfo("保存成功！");
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
        });
      }

      //解决转换json时的bug
      function preventCircularJson(source, censoredMessage, censorTheseItems) {
        censorTheseItems = censorTheseItems || [source];
        censoredMessage = censoredMessage || "CIRCULAR_REFERENCE_REMOVED";
        var recursiveItems = {};
        var ret = {};
        for (var key in source) {
          var value = source[key];
          if (typeof value == "object") {
            recursiveItems[key] = value;
          } else {
            ret[key] = value;
          }
        }
        var censorChildItems = [];
        for (var key in recursiveItems) {
          var value = source[key];
          censorChildItems.push(value);
        }
        for (var key in recursiveItems) {
          var value = source[key];
          var censored = false;
          censorTheseItems.forEach(function(item) {
            if (item === value) {
              censored = true;
            }
          });
          if (censored) {
            value = censoredMessage;
          } else {
            value = preventCircularJson(
              value,
              censoredMessage,
              censorChildItems.concat(censorTheseItems)
            );
          }
          ret[key] = value;
        }
        return ret;
      }

      $scope.addFeatureOrFlow = function(data) {
        var modalInstance = $modal.open({
          animation: true,
          templateUrl: getTemplateUrl(
            "matrix/component/tpl/flowNodeModal.html"
          ),
          controller: "addFeatureOrFlowCtrl",
          keyboard: true,
          backdrop: true,
          resolve: {
            data: function() {
              return data;
            }
          }
        });

        modalInstance.result.then(
          function(nodeItem) {
            if (data.type == "Feature") {
              if (nodeItem.featureType == "new") {
                addNewFeature(nodeItem, data);
              } else {
                getReqFeatureList(nodeItem, data);
              }
            } else {
              if (nodeItem.featureType == "new") {
                addNewFlow(nodeItem, data);
              } else {
                getReqFlowList(nodeItem, data);
              }
            }
          },
          function(reason) {
            console.log("关闭modal");
          }
        );
      };

      function addNewFeature(nodeItem, data) {
        delete nodeItem.featureType;
        var url = matrix_product_url + "/feature/createFeature";
        $http({
          method: "post",
          url: url,
          data: JSON.stringify(nodeItem),
          headers: { "Content-Type": "application/json" }
        }).success(function(result) {
          if (result.code == "0") {
            nodeItem.id = result.data || "";
            $rootScope.toasterSuccessInfo("创建成功！");
            getReqFeatureList(nodeItem, data, "new");
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
        });
      }

      function addNewFlow(nodeItem, data) {
        delete nodeItem.featureType;
        var url = matrix_product_url + "/flow/createFlow";
        $http({
          method: "post",
          url: url,
          data: JSON.stringify(nodeItem),
          headers: { "Content-Type": "application/json" }
        }).success(function(result) {
          if (result.code == "0") {
            $rootScope.toasterSuccessInfo("创建成功！");
            nodeItem.id = result.data.id;
            nodeItem.flowname = nodeItem.name;
            getReqFlowList(nodeItem, data);
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
        });
      }

      function setFeatureOrFlow(reqRelateList, nodeItem, data, nodeType) {
        // nodeItem = isRealte(reqRelateList, nodeItem);
        nodeItem.isRelate = "oldItem";
        if (data.type == "Feature" && !nodeItem.featureKind) {
          nodeItem.featureKind = data.featureKind;
        }
        nodeItem.isOldItem = "oldItem";
        data.type == "Feature" ? (nodeItem.feature_id = nodeItem.id) : (nodeItem.flow_id = nodeItem.id);
        if (data.status == "update") {
          var e = $scope.draw.diagramScope.e;
          var partData = $scope.draw.diagramScope.partData;
          var category = $scope.draw.diagramScope.category;
          nodeItem.isNew = category == "Feature" ? "修改节点" : "修改流程";
          var data = {};
          $scope.draw.diagramScope.changeNodeCategory(e, partData, category, data, nodeItem);
        } else {
          var obj = $scope.draw.diagramScope.obj;
          var category = $scope.draw.diagramScope.category;
          nodeItem.isNew = category == "Feature" ? "新增节点" : "新增流程";
          $scope.draw.diagramScope.propAddNodeAndLink(obj, category, nodeItem);
        }
      }

      function isRealte(data, nodeItem) {
        nodeItem.isRelate = "";
        var isRelate = lodash.findIndex(data, function(d) {
          return d.id == nodeItem.id;
        });
        if (isRelate != -1) {
          nodeItem.isRelate = "isRelate";
        } else {
          nodeItem.isRelate = "isAddNode";
        }
        return nodeItem;
      }

      // 获取关联流程（非节点变更）
      function getReqFlowList(nodeItem, data) {
        var url =
          matrix_product_url + "/flow/getReqFlows?reqId=" + $stateParams.reqId;
        $http.get(url).success(function(result) {
          if (result.code == "0") {
            angular.forEach(result.data, function(flow) {
              if (flow.status != "NODE_UPDATE") {
                $scope.info.reqFlowList.push(flow);
              }
            });
            setFeatureOrFlow($scope.info.reqFlowList, nodeItem, data);
          } else {
            $rootScope.toasterErrorInfo("获取关联流程数据错误！");
          }
        });
      }

      // 获取关联功能点
      function getReqFeatureList(nodeItem, data, nodeType) {
        var url =
          matrix_product_url +
          "/feature/getAllFeatureForReq?reqId=" +
          $stateParams.reqId;
        $http.get(url).success(function(result) {
          if (result.code == "0") {
            $scope.info.reqFeatureList = result.data || [];
            setFeatureOrFlow($scope.info.reqFeatureList, nodeItem, data, nodeType);
          } else {
            $rootScope.toasterErrorInfo("获取关联功能点数据错误！");
          }
        });
      }

      // 获取点击的节点详细信息
      $scope.getNodeDetails = function(data) {
        console.log("获取点击节点信息");
        console.log(data);
      };

      //获取全部的功能点（用于选择功能点然后添加到流程图模板组件中）
      function getFeatureList() {
        var url =
          matrix_product_url + "/feature/getFeature?withPrototype=1&withAttr=0";
        $http.get(url).success(function(result) {
          if (result.code == "0") {
            var featureList = [];
            angular.forEach(result.data, function(res, index) {
              if (res.path) {
                var itemName =
                  res.type == "PROTOTYPE"
                    ? res.featureName +
                      "【" +
                      lodash.last(res.path.split("/")) +
                      "】"
                    : res.featureName;
                var item = {
                  crtname: "..." + res.path.substring(17),
                  category: "formula",
                  text: res.path
                    ? "F: " + itemName
                    : "F: " + lodash.last(res.path.split("/")),
                  feature_id: res.id,
                  nodeType: "feature",
                  //isHighlighted: res.status ? (res.status == "ADD" ? true : false) : false,
                  isHighlighted: "",
                  feature_info: {
                    id: res.id,
                    sys: res.sys,
                    type: res.type,
                    title: res.featureName ? itemName : "",
                    status: res.status ? res.status : "ADD",
                    relate_id: "",
                    relate_type: "",
                    relate_name:
                      res.type == "PROTOTYPE"
                        ? lodash.last(res.path.split("/"))
                        : res.featureName,
                    relate_path: res.path,
                    bussiness_line_id: res.bussinessLine
                  },
                  status: res.status ? res.status : "ADD",
                  sys: res.sys,
                  relate_name:
                    res.type == "PROTOTYPE"
                      ? lodash.last(res.path.split("/"))
                      : res.featureName,
                  relate_path: res.path
                };
                // resultLegendArray.push(item);
                featureList.push(item);
              }
            });
            $scope.draw.featureList = featureList;
            //var orderByLegend = $filter('orderBy')(resultLegendArray, "index", false);
            //$scope.draw.paModel = new go.GraphLinksModel(orderByLegend);
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
        });
      }

      //获取流程树
      function getFlowTree() {
        var url = matrix_product_url + "/flow/getFlowTree";
        $http.get(url).success(function(result) {
          if (result.code == "0") {
            $scope.draw.flowArray = setFlowLegendPath(
              result.data.children,
              [],
              ""
            );
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
        });
      }

      //解析流程树数据
      function setFlowLegendPath(featureTree, resultFeatureArray, featureName) {
        angular.forEach(featureTree, function(feature, index) {
          var pathUrl = featureName
            ? featureName + "/" + feature.name
            : feature.name;
          //type  1: frame   2: flow
          if (feature.type == "2") {
            var featureItem = {
              crtname: pathUrl,
              category: "formula",
              text: pathUrl ? "P: " + pathUrl : "",
              nodeType: "flow",
              isHighlighted: "flow",
              feature_info: {
                id: feature.id,
                sys: "",
                type: feature.type,
                title: pathUrl ? pathUrl : "",
                status: feature.status ? feature.status : "ADD",
                relate_id: "",
                relate_type: "",
                relate_name: pathUrl,
                relate_path: pathUrl,
                bussiness_line_id: feature.metaData.bussinessId
              },
              status: feature.status ? feature.status : "ADD",
              sys: "",
              relate_name: pathUrl,
              relate_path: pathUrl
            };
            resultFeatureArray.push(featureItem);
          }
          if (feature.children && feature.children.length) {
            setFlowLegendPath(
              feature.children,
              resultFeatureArray,
              feature.name
            );
          }
        });
        return resultFeatureArray;
      }

      //选择图例，然后添加到图例工具栏中
      $scope.addLegendItem = function() {
        if (!$scope.draw.sltLegendItem) {
          $rootScope.toasterWarningInfo("添加功能点不能为空！");
          return false;
        }
        $scope.draw.nodeDataArray = angular.copy(
          $scope.draw.model.nodeDataArray
        );
        $scope.draw.linkDataArray = angular.copy(
          $scope.draw.model.linkDataArray
        );
        $scope.draw.model = new go.GraphLinksModel(
          $scope.draw.nodeDataArray,
          $scope.draw.linkDataArray
        );
        var resultLegendArray = $scope.draw.nodeDataArray
          ? $scope.draw.nodeDataArray
          : [];

        //根据已有图例的坐标，设置新增图例的坐标为左上角
        var minLocX = lodash
          .chain(resultLegendArray)
          .pluck("loc")
          .map(function(loc) {
            var splitLoc = loc.split(" ");
            return parseInt(splitLoc[0]);
          })
          .min()
          .value();
        var minLocY = lodash
          .chain(resultLegendArray)
          .pluck("loc")
          .map(function(loc) {
            var splitLoc = loc.split(" ");
            return parseInt(splitLoc[1]);
          })
          .min()
          .value();
        // var LocY = parseInt(minLocY) + 70*$scope.draw.addNum; //设置新增图例根据初始化一个个往下添加
        $scope.draw.sltLegendItem.loc = "" + minLocX + " " + minLocY + "";

        angular.forEach(resultLegendArray, function(result) {
          var isEqailHashId = lodash.findIndex(resultLegendArray, function(
            resLegend
          ) {
            return result.__gohashid == resLegend.__gohashid;
          });
          if (isEqailHashId != -1) {
            result.__gohashid++;
          }
        });
        if ($scope.draw.isFeature) {
          angular.forEach($scope.draw.featureList, function(res) {
            if (res.feature_id) {
              if (res.feature_id == $scope.draw.sltLegendItem.feature_id) {
                res.isFeature =
                  $scope.draw.sltFlowTypeItem.type == "feature" ? true : false;
                res.isHighlighted = "ADD";
                //res.nodeStatus = "ADD";
                resultLegendArray.push(res);
              }
            }
          });
        } else {
          $scope.draw.sltLegendItem.isFeature =
            $scope.draw.sltFlowTypeItem.type == "feature" ? true : false;
          resultLegendArray.push($scope.draw.sltLegendItem);
        }
        $scope.draw.customFeatureArray = resultLegendArray;
        $scope.draw.model = new go.GraphLinksModel(
          resultLegendArray,
          $scope.draw.linkDataArray
        );
        $scope.draw.addNum++;
        $scope.draw.sltLegendItem = "";
      };

      //switch按钮状态   功能点：true   流程：false
      $scope.switchChange = function(item) {
        $scope.draw.sltLegendItem = "";
        $scope.draw.isFeature = item.type == "feature" ? true : false;
      };

      // 查询
      $scope.searchNode = function() {
        $scope.draw.diagramScope.searchNode($scope.draw.input);
      };

      //节点回车查询
      $scope.myKeyup = function(e) {
        var keyCode = window.event ? e.keyCode : e.which;
        if (keyCode == 13) {
          $scope.draw.diagramScope.searchNode($scope.draw.input);
        }
      };
      // window.location.href = "#/app/matrix/drawFlow/-1/"+ nodeData.id;
    };

    //历史记录
    $scope.goToHistory = function(nodeData) {
      $scope.info.showName = "history";
      $rootScope.initHeaderElements($scope.settings);
      var refreshBtn = {
        show: true,
        label: "刷新",
        func: function() {
          getFlowDetails();
        }
      };
      var backUrl = {
        show: true,
        label: "返回",
        func: function() {
          $scope.info.showName = "map";
          $scope.setBreadcrumb();
          $scope.initSetting();
        }
      };
      $scope.setBreadcrumb("历史记录");
      $scope.settings.btnEle.push(refreshBtn);
      $scope.settings.btnEle.push(backUrl);
      // window.location.href = "#/app/matrix/flowHistory/"+ nodeData.id;
      $scope.history = {
        currentReqId: "-1",
        diagram: null,
        showVersion: false,
        flowDetails: "", //流程详情
        prevNameVersion: "", //流程详细信息（流程名称+版本号）
        nextNameVersion: "",
        sltPrevItem: "",
        sltNextItem: "",
        flowVersions: [],
        prevDetails: [],
        nextDetails: [],
        historyData: {},
        compareData: {}
      };

      $scope.history.modelVersion = new go.TreeModel([]);
      $scope.history.model = new go.GraphLinksModel([], []);
      $scope.history.modelMaster = new go.GraphLinksModel([], []);
      $scope.history.mDiagram = null;
      $scope.history.mDiagramMaster = null; //设置画布只读时需要
      $scope.history.paModel = new go.GraphLinksModel([]);
      $scope.history.paModelMaster = new go.GraphLinksModel([]);

      getFlowDetails();

      //获取流程详细信息
      function getFlowDetails() {
        var url =
          matrix_product_url +
          "/flow/getFlowDetail?flowId=" +
          nodeData.id +
          "&reqId=-1";
        $rootScope.showPanelLoading();
        $http.get(url).success(function(result) {
          if (result.code == "0") {
            if (!result.data) {
              $rootScope.toasterWarningInfo("获取流程详情数据错误！");
              return false;
            }
            $scope.history.flowDetails = result.data;
            getFlowVersions();
            $rootScope.hidePanelLoading();
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
        });
      }
      //返回视图
      $scope.view = function() {
        $scope.history.showVersion = false;
      };

      //获取流程版本
      function getFlowVersions() {
        var url =
          matrix_product_url + "/flow/getAllFlowVersions?flowId=" + nodeData.id;
        $rootScope.showPanelLoading();
        $http.get(url).success(function(result) {
          if (result.code == "0") {
            $scope.history.showVersion = false;
            $scope.history.sltPrevItem = "";
            $scope.history.sltNextItem = "";
            var versionArray = [];
            if (result.data.master.length > 0) {
              angular.forEach(result.data.master, function(master) {
                versionArray.push(master);
              });
            }
            if (result.data.req.length > 0) {
              angular.forEach(result.data.req, function(req) {
                versionArray.push(req);
              });
            }
            if (!versionArray) {
              $rootScope.toasterWarningInfo("流程版本数据为空！");
              return false;
            }
            angular.forEach(versionArray, function(ver) {
              ver.crtname =
                "【" +
                (ver.reqId == "-1" ? "master" : ver.reqName) +
                "】" +
                ver.version;
            });
            $scope.history.flowVersions = versionArray;
            angular.forEach(versionArray, function(ver) {
              ver.crtname =
                "【" +
                (ver.reqId == "-1" ? "master" : ver.reqName) +
                "】" +
                ver.version;
            });
            $scope.history.flowVersions = versionArray;
            var vNodeItemArray = [];
            var vLinkItemArray = [];
            var masterLinkArray = [];
            var filterLinkArray = [];
            angular.forEach(versionArray, function(version) {
              var vNodeItem = {
                num: lodash.trim(
                  version.version,
                  version.reqId == "-1" ? "V" : "v"
                ),
                key:
                  version.reqId == "-1"
                    ? version.attrs.version
                    : lodash.trim(version.version, "v"),
                name:
                  version.reqId == "-1"
                    ? version.crtname
                    : "【" + version.reqName + "】" + version.version,
                type: version.reqId == "-1" ? "master" : "req",
                attrs: version.attrs
              };
              if (version.reqId != "-1") {
                vNodeItem.key =
                  lodash.trim(version.version, "v") + version.reqId;
                vNodeItem.reqId = version.reqId;
                filterLinkArray.push(vNodeItem);
              }
              vNodeItemArray.push(vNodeItem);
            });

            vNodeItemArray = $filter("orderBy")(
              $filter("orderBy")(vNodeItemArray, "num", false),
              "type",
              false
            );
            angular.forEach(vNodeItemArray, function(vNodeItem, index) {
              if (vNodeItem.type == "master") {
                if (vNodeItem.num == "online") {
                  return;
                }
                var vLinkItem = {
                  from: vNodeItem.attrs.version,
                  to: vNodeItemArray[index + 1].attrs.version
                };
                vLinkItemArray.push(vLinkItem);
              }
            });

            var reqArray = [];
            var filterMasterLink = [];
            filterMasterLink = $filter("orderBy")(
              masterLinkArray,
              "num",
              false
            );
            for (var i = 0; i < filterMasterLink.length; i++) {
              if (i + 1 != filterMasterLink.length) {
                vLinkItemArray.push({
                  from: filterMasterLink[i].key,
                  to: filterMasterLink[i + 1].key
                });
              }
            }
            angular.forEach(filterLinkArray, function(filterItem) {
              var reqIndex = lodash.findIndex(reqArray, function(req) {
                return filterItem.reqId == req.reqId;
              });
              if (reqIndex == -1) {
                reqArray.push({ reqId: filterItem.reqId });
              }
            });
            angular.forEach(reqArray, function(req) {
              var crtReqArray = [];
              angular.forEach(filterLinkArray, function(item) {
                if (item.reqId == req.reqId) {
                  crtReqArray.push(item);
                }
              });
              for (var i = 0; i < crtReqArray.length; i++) {
                var linkList = [];
                angular.forEach(filterLinkArray, function(fl) {
                  if (fl.reqId == crtReqArray[i].reqId) {
                    linkList.push(fl);
                  }
                });
                var firstReqItem = lodash.first(
                  $filter("orderBy")(linkList, "num", false)
                );
                if (crtReqArray[i].reqId == req.reqId) {
                  if (i + 1 != crtReqArray.length) {
                    vLinkItemArray.push({
                      from: crtReqArray[i].num + req.reqId,
                      to: crtReqArray[i + 1].num + req.reqId
                    });
                  }
                  if (crtReqArray[i].num == firstReqItem.num) {
                    vLinkItemArray.push({
                      from: crtReqArray[i].attrs.from_master_version,
                      to: crtReqArray[i].num + req.reqId
                    });
                  }
                  if (i == crtReqArray.length - 1) {
                    vLinkItemArray.push({
                      from: crtReqArray[i].num + req.reqId,
                      to: crtReqArray[i].attrs.to_master_version
                    });
                  }
                }
              }
            });
            $scope.history.modelVersion = new go.GraphLinksModel(
              vNodeItemArray,
              vLinkItemArray
            );
          } else {
            $rootScope.toasterErrorInfo(result.desc);
          }
          $rootScope.hidePanelLoading();
        });
      }

      //比较历史
      $scope.compareHistory = function() {
        if (!$scope.history.sltPrevItem) {
          $rootScope.toasterWarningInfo("版本数据不能为空，请选择！");
          return false;
        }
        if (!$scope.history.sltNextItem) {
          $rootScope.toasterWarningInfo("版本数据不能为空，请选择！");
          return false;
        }
        $scope.showHistory = false;
        $timeout(function() {
          getPrevHistory($scope.history.sltPrevItem);
          getNextHistory($scope.history.sltNextItem);
          $scope.history.showVersion = true;
          $scope.showHistory = true;
        });
      };

      //获取前一个版本的历史数据
      function getPrevHistory(prevHistoryDetails) {
        $scope.history.prevNameVersion = {
          name: prevHistoryDetails.reqName,
          version: prevHistoryDetails.version
        };
        $scope.history.historyData = {
          nodeDataArray: prevHistoryDetails.attrs
            ? prevHistoryDetails.attrs.nodeDataArray || []
            : [],
          linkDataArray: prevHistoryDetails.attrs
            ? prevHistoryDetails.attrs.linkDataArray || []
            : []
        };
      }

      //获取后一个版本的历史数据
      function getNextHistory(nextHistoryDetails) {
        $scope.history.nextNameVersion = {
          name: nextHistoryDetails.reqName,
          version: nextHistoryDetails.version
        };
        $scope.history.compareData = {
          nodeDataArray: nextHistoryDetails.attrs
            ? nextHistoryDetails.attrs.nodeDataArray || []
            : [],
          linkDataArray: nextHistoryDetails.attrs
            ? nextHistoryDetails.attrs.linkDataArray || []
            : []
        };
      }
    };

    //删除节点
    $scope.removeNode = function(nodeData) {
      var bodyText =
        nodeData.type == "1"
          ? "确定要删除流程框架【" + nodeData.metaData.name + "】"
          : "确定要删除流程【" + nodeData.metaData.name + "】";
      var confirmParams = {
        titleData: "提示",
        bodyData: bodyText
      };
      $rootScope.confirmWindow(confirmParams, function(data) {
        if (data) {
          var urlPath =
            nodeData.type == "1"
              ? "/flowFrame/deleteFlowFrame?flowFrameId="
              : "/flow/deleteFlow?flowId=";
          var url = matrix_product_url + urlPath + nodeData.id;
          $http.get(url).success(function(result) {
            if (result.code == "0") {
              // getFlowTree();
              $scope.info.diagramScope.deleteNode(
                setFlowTree([nodeData], [])[0]
              );
              $rootScope.toasterSuccessInfo("删除成功！");
            } else {
              $rootScope.toasterErrorInfo(result.desc);
            }
          });
        }
      });
    };

    $scope.info.isSort = false;
    $scope.info.childs = [];
    $scope.toSort = function() {
      //获取选中节点的父节点下所有字节点
      $scope.info.childs = $scope.info.diagramScope.getChildsData();
      console.log("KKKKKKKKKKKKKKKK" + JSON.stringify($scope.info.childs));
      //隐藏flow 展示排序
      $scope.info.isSort = true;
      //修改返回按钮
      $rootScope.initHeaderElements($scope.settings);
      $scope.settings.btnEle.push({
        show: true,
        label: "返回",
        func: function() {
          $scope.initSetting();
          $scope.info.isSort = false;
        }
      });
    };
    //调整节点顺序
    $scope.sortNode = function(list) {
      var ids = "";
      angular.forEach(list, function(data) {
        ids += data.id + ",";
      });
      $http
        .post(matrix_product_url + "/flowFrame/childSort", {
          flowFrameId: $scope.info.nodeDetails.parentId,
          sort: ids
        })
        .success(function() {
          getFlowTree();
          $scope.initSetting();
          $scope.info.isSort = false;
        })
        .error(function() {
          $scope.initSetting();
          $scope.info.isSort = false;
        });
    };
  }
]);

app.controller("addFeatureOrFlowCtrl", [
  "$scope",
  "$rootScope",
  "$modalInstance",
  "$http",
  "lodash",
  "data",
  function($scope, $rootScope, $modalInstance, $http, lodash, data) {
    var flagName = "";
    switch(data.flag) {
        case "JUMP": 
            flagName = "【展示页面】";
            break;
        case "UI": 
            flagName = "【页面交互】";
            break;
        case "FORM": 
            flagName = "【信息提交】";
            break;
        case "SCAN": 
            flagName = "【扫描枪扫描】";
            break;
    }
    $scope.flowTypeName =
      "引入" + (data.type == "Feature" ? "功能点" + flagName : "流程");
    $scope.promptName = data.type == "Feature" ? "请选择功能点" : "请选择流程";
    $scope.type = data.type;
    $scope.isAddFeatureOrFlow = true;
    $scope.sysArray = config_flow_legend_sys;
    $scope.ffDataArray = [];
    $scope.currentRadio = "select";
    $scope.addFeatureOrFlowName = "";
    $scope.treeData = [];
    $scope.hideIsMaster = true;
    $scope.sltBranch = [];
    $scope.sltSysItem = lodash.first(config_flow_legend_sys);
    $scope.featureKindArray = config_feature_details_type;
    $scope.sltKindItem = lodash.findWhere(config_feature_details_type, {
      key: data.flag
    });

    data.type == "Feature"
      ? getAllFeature($scope.sltSysItem.key, $scope.sltKindItem.key)
      : getFlowTree();

    $scope.switchRadio = function(type) {
      $scope.sltKindItem = lodash.findWhere(config_feature_details_type, {
        key: data.flag
      });
      $scope.currentRadio = type;
      if (type == "new") {
        data.type == "Feature" ? getFeatureTree($scope.sltSysItem.key) : getFlowFrameTree();
      }
    };

    $scope.switchKind = function(item) {
      console.log(item);
      $scope.sltKindItem = item;
      $scope.sltLegendItem = "";
      getAllFeature($scope.sltSysItem.key || lodash.first(config_flow_legend_sys).key, item.key);
    };

    $scope.switchSys = function(item) {
      $scope.sltSysItem = item;
      $scope.sltLegendItem = "";
      $scope.currentRadio == "new"
        ? getFeatureTree(item.crtname)
        : getAllFeature(item.crtname, $scope.sltKindItem.key);
    };

    if (data.type != "Feature") {
      getFlowFrameTree();
    }

    function getFeatureTree(sys) {
      $("#showNewLoading").css("display", "block");
      $("#newFeatureTree").css("display", "none");
      var url =
        matrix_product_url + "/feature/getFeatureBaseTree?featureSys=" + sys;
      $http.get(url).success(function(result) {
        if (result.code == "0") {
          $scope.treeData = result.data[0].children;
        } else {
          $rootScope.toasterErrorInfo(result.desc);
        }
        $("#showNewLoading").css("display", "none");
        $("#newFeatureTree").css("display", "block");
      });
    }

    //获取流程架构树数据
    function getFlowFrameTree() {
      $("#showNewLoading").css("display", "block");
      $("#newFeatureTree").css("display", "none");
      var url = matrix_product_url + "/flowFrame/getFlowFrameTree";
      $http.get(url).success(function(result) {
        if (result.code == "0") {
          $scope.treeData = result.data.children;
        } else {
          $rootScope.toasterErrorInfo(result.desc);
        }
        $("#showNewLoading").css("display", "none");
        $("#newFeatureTree").css("display", "block");
      });
    }

    $scope.selectProductLine = function(item, selectedItems) {
      $scope.sltBranch = item;
    };

    // 获取功能点数据
    function getAllFeature(sys, featureKind) {
      $("#showLoading").css("display", "block");
      $("#showList").css("display", "none");
      $scope.ffDataArray = new Array();
      var url =
        matrix_product_url + "/feature/getFeature?withPrototype=1&withAttr=0";
      $http.get(url).success(function(result) {
        if (result.code == "0") {
          angular.forEach(result.data, function(data) {
            data.crtname = (data.path || "") + "/" + (data.featureName || "");
          });
          angular.forEach(result.data, function(resData) {
            if (sys) {
              if (resData.sys == sys) {
                if (featureKind == resData.featureKind) {
                  $scope.ffDataArray.push(resData);
                }
              }
            } else {
              $scope.ffDataArray.push(resData);
            }
          });
        } else {
          $rootScope.toasterErrorInfo("获取功能点数据错误！");
        }
        $("#showLoading").css("display", "none");
        $("#showList").css("display", "block");
      });
    }

    // 获取流程树
    function getFlowTree() {
      $("#showLoading").css("display", "block");
      $("#showList").css("display", "none");
      var url = matrix_product_url + "/flow/getFlowTree";
      $http.get(url).success(function(result) {
        if (result.code == "0") {
          $scope.ffDataArray = setFlowLegendPath(result.data.children, [], "");
        } else {
          $rootScope.toasterErrorInfo(result.desc);
        }
        $("#showLoading").css("display", "none");
        $("#showList").css("display", "block");
      });
    }

    // 解析流程树数据
    function setFlowLegendPath(featureTree, resultFeatureArray, featureName) {
      angular.forEach(featureTree, function(feature, index) {
        var pathUrl = featureName
          ? featureName + "/" + feature.name
          : feature.name;
        //type  1: frame   2: flow
        if (feature.type == "2") {
          var featureItem = {
            crtname: pathUrl,
            flowname: feature.name,
            status: feature.status,
            path: featureName
          };
          resultFeatureArray.push(featureItem);
        }
        if (feature.children && feature.children.length) {
          setFlowLegendPath(feature.children, resultFeatureArray, feature.name);
        }
      });
      return resultFeatureArray;
    }

    $scope.createClick = function() {
      if ($scope.currentRadio == "select") {
        if (!$scope.sltLegendItem) {
          $rootScope.toasterWarningInfo("引入节点数据不能为空！");
          return false;
        }
      }
      if ($scope.currentRadio == "new") {
        if (data.type == "Feature") {
          if (!$scope.sltSysItem.crtname) {
            $rootScope.toasterWarningInfo("请选择系统类型！");
            return false;
          }
          if ($scope.sltBranch.nodeType != "page") {
            $rootScope.toasterWarningInfo("父节点类型错误，请重新选择！");
            return false;
          }
          if (lodash.trim($scope.addFeatureOrFlowName) == "") {
            $rootScope.toasterWarningInfo("新建名称不能为空！");
            return false;
          }
          var featureInfo = {
            featureType: "new",
            attr: {
              feature_name: lodash.trim($scope.addFeatureOrFlowName)
            },
            featureName: lodash.trim($scope.addFeatureOrFlowName),
            featureKind: $scope.sltKindItem.key,
            originalPageId: $scope.sltBranch.id,
            pageId: $scope.sltBranch.id,
            path:
              ($scope.sltBranch.metaData.menuPath || "") +
              "/" +
              ($scope.sltBranch.name || ""),
            reqId: "-1",
            sys: $scope.sltSysItem.crtname
          };
        } else {
          if ($scope.sltBranch.type != "FLOW_FRAME") {
            $rootScope.toasterWarningInfo("父节点类型错误，请重新选择！");
            return false;
          }
          if (lodash.trim($scope.addFeatureOrFlowName) == "") {
            $rootScope.toasterWarningInfo("新建名称不能为空！");
            return false;
          }
          var flowInfo = {
            featureType: "new",
            attrs: {},
            bussinessId: $scope.sltBranch.id,
            name: lodash.trim($scope.addFeatureOrFlowName),
            reqId: "-1",
            status: "ADD",
            flowName: lodash.trim($scope.addFeatureOrFlowName),
            path: $scope.sltBranch.name
          };
        }
      }

      $modalInstance.close(
        $scope.currentRadio == "select"
          ? $scope.sltLegendItem
          : data.type == "Feature"
            ? featureInfo
            : flowInfo
      );
    };

    $scope.cancelClick = function() {
      $modalInstance.dismiss("cancel");
    };
  }
]);

app.controller("editFlowCtrl", [
  "$scope",
  "$rootScope",
  "$modalInstance",
  "$http",
  "lodash",
  "type",
  "status",
  "nodeData",
  function(
    $scope,
    $rootScope,
    $modalInstance,
    $http,
    lodash,
    type,
    status,
    nodeData
  ) {
    $scope.flowName = type != "add" ? nodeData.name : "";
    $scope.flowType = type;
    $scope.originData = []; //过滤后的流程架构树数据
    $scope.sltFrameItem = ""; //已选择的frame
    $scope.dataCrtValue = "请选择节点";
    $scope.flowTypeName =
      status == "frame"
        ? type == "add"
          ? "新建流程架构"
          : type == "move"
            ? "移动流程架构"
            : "编辑流程架构"
        : type == "add"
          ? "新建流程"
          : type == "move"
            ? "移动流程"
            : "编辑流程";

    getFrameTree();

    //获取流程架构树
    function getFrameTree() {
      var url = matrix_product_url + "/flowFrame/getFlowFrameTree";
      $http.get(url).success(function(result) {
        if (result.code == "0") {
          $scope.originData = filterFrameTree([result.data], []);
        } else {
          $rootScope.toasterErrorInfo(result.desc);
        }
      });
    }

    //过滤流程架构
    function filterFrameTree(frameTree, frameArray, frameName) {
      angular.forEach(frameTree, function(frame) {
        frame.crtname = frameName ? frameName + "/" + frame.name : frame.name;
        if (frame.id == nodeData.parentId) {
          return;
        }
        if (frame.level == nodeData.level - 1 && frame.type == "FLOW_FRAME") {
          frameArray.push(frame);
        } else if (
          frame.level < nodeData.level - 1 &&
          frame.children &&
          frame.children.length > 0
        ) {
          filterFrameTree(frame.children, frameArray, frame.crtname);
        }
      });
      return frameArray;
    }

    //确认事件
    $scope.createClick = function() {
      if (type != "move") {
        if ($scope.flowName == nodeData.name) {
          $rootScope.toasterWarningInfo("名称不能重复！");
          return false;
        }
      }
      $modalInstance.close(
        type == "move" ? $scope.sltFrameItem : $scope.flowName
      );
    };

    //取消事件
    $scope.cancelClick = function() {
      $modalInstance.dismiss("cancel");
    };
  }
]);

//历史
app.directive("versionDiagram", function($http, $rootScope, lodash) {
  return {
    restrict: "E",
    replace: true,
    template:
      '<div class="col-sm-12">' +
      '    <div id="myVersionDiagram" class="col-sm-12" style="background-color: white; border: solid 1px #cfdadd; width: 100%; height: 600px;"></div>' +
      "</div>",
    scope: {
      model: "=goModel"
    },
    link: function(scope, ele, attr, ctrl) {
      var $ = go.GraphObject.make;
      //定义diagram
      var diagram = $(go.Diagram, "myVersionDiagram", {
        "toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom,
        initialContentAlignment: go.Spot.MiddleTop,
        padding: 10,
        isReadOnly: true, //只读
        layout: $(go.LayeredDigraphLayout, {
          direction: 90,
          layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource
        }),
        "undoManager.isEnabled": true
      });

      //给diagram设置数据
      scope.$watch("model", function(newmodel) {
        var oldmodel = diagram.model;
        if (oldmodel !== newmodel) {
          diagram.model = newmodel;
        }
      });

      diagram.nodeTemplate = $(
        go.Node,
        go.Panel.Vertical,
        { portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" },
        $(
          go.Panel,
          go.Panel.Auto,
          $(
            go.Shape,
            { fill: "white", minSize: new go.Size(30, 30), strokeWidth: 0 },
            { cursor: "pointer" },
            new go.Binding("fill", "type", function(type) {
              return type == "master" ? "#d4e2ef" : "#e6f0d8";
            })
          ),
          $(
            go.TextBlock,
            { margin: 2 },
            { fromLinkable: false, toLinkable: false },
            new go.Binding("text", "name")
          )
        ),
        $(
          "Button",
          { visible: false },
          new go.Binding("visible", "isTreeLeaf", function(leaf) {
            return !leaf;
          }).ofObject(),
          $(
            go.Shape,
            {
              name: "ButtonIcon",
              figure: "MinusLine",
              desiredSize: new go.Size(6, 6)
            },
            new go.Binding("figure", "isCollapsed", function(collapsed) {
              return collapsed ? "PlusLine" : "MinusLine";
            })
          ),
          {
            click: function(e, obj) {
              e.diagram.startTransaction();
              var node = obj.part;
              if (node.data.isCollapsed) {
                expandFrom(node, node);
              } else {
                collapseFrom(node, node);
              }
              e.diagram.commitTransaction("toggled visibility of dependencies");
            }
          }
        )
      );

      function collapseFrom(node, start) {
        if (node.data.isCollapsed) return;
        node.diagram.model.setDataProperty(node.data, "isCollapsed", true);
        if (node !== start) node.visible = false;
        node.findNodesOutOf().each(collapseFrom);
      }

      function expandFrom(node, start) {
        if (!node.data.isCollapsed) return;
        node.diagram.model.setDataProperty(node.data, "isCollapsed", false);
        if (node !== start) node.visible = true;
        node.findNodesOutOf().each(expandFrom);
      }

      //连接线模板
      diagram.linkTemplate = $(
        go.Link,
        { relinkableFrom: true, relinkableTo: true, corner: 10 },
        $(go.Shape),
        $(go.Shape, { toArrow: "Standard" })
      );
    }
  };
});
